/*
 * matrix_test.cpp
 *
 *  Created on: 08/06/2011
 *      Author: Yotam and Gilaad
 */

#include <assert.h>
#include <stdarg.h>
#include "../matrix.h"
#include "../mtm.h"
#include "mtmtest.h"
using namespace std;

Matrix initMatrix( int rows, int columns, ... )
{
	Matrix result( rows, columns );
	va_list vl;
	va_start( vl, columns );

	for( int r = 1; r <= rows; ++r )
	{
		for( int c = 1; c <= columns; ++c )
		{
			double cell = va_arg(vl,double);
			result(r, c) = cell;
		}
	}
	va_end( vl );
	return result;
}

bool testPlacementLeftVector() {
	Matrix A = Matrix::createUnit(3);
	double num = A(1,1);
	ASSERT_EQUALS(num , A(1,1));
	ASSERT_THROW(MatrixExceptions::IndexOutOfRange, A(5,2));
	return true;
}
bool testPlacementRightVector() {
	Matrix A = initMatrix( 3, 3,
			1.0, 4.3, 5.2,
			3.9, 4.4, 1.3,
			7.2, 5.6, 7.7 );
	double zero = 0.0;
	A(1,1) = zero;
	Matrix B = initMatrix ( 3, 3,
			0.0, 4.3, 5.2,
			3.9, 4.4, 1.3,
			7.2, 5.6, 7.7 );
	ASSERT_EQUALS(A,B);
	ASSERT_THROW(MatrixExceptions::IndexOutOfRange, A(5,2));
	return true;
}
bool testDestructMatrix() {
	// checked in valgrind
	return true;
}
bool testEqualOperator() {
	Matrix A = initMatrix( 2, 2,
			1.1, 2.2,
			3.3, 4.4 );
	Matrix B(2,2);
	Matrix C(3,3);
	ASSERT_EQUALS(A,A);
	ASSERT_EQUALS(false, A==B);
	ASSERT_THROW(MatrixExceptions::BadDimensions, A==C);
	return true;
}
bool testNotEqualOperator() {
	Matrix A = initMatrix( 2, 3,
			2.9, 1.4, 4.8,
			1.1, 7.8, 9.9 );
	Matrix B(2,3);
	Matrix C(3,3);
	ASSERT_EQUALS(true, A!=B);
	ASSERT_NO_THROW(A!=B);
	ASSERT_THROW(MatrixExceptions::BadDimensions, A!=C);
	return true;
}
bool testAssignmentOperator() {
	Matrix A(2,2);
	Matrix B = initMatrix( 2, 2,
			1.5,  2.5,
			4.5, -0.5 );
	Matrix C = A;
	Matrix D(5,5);
	ASSERT_NO_THROW(C = A + B);
	ASSERT_EQUALS(B,C);
	return true;
}
bool testPlusEqualOperator() {
	Matrix A = initMatrix( 3, 3,
			1.2, 3.2, 5.2,
			4.3, 5.3, 6.3,
			3.4, 3.4, 8.4 );
	Matrix B = initMatrix( 3, 3,
			-1.0, -1.0, -1.0,
			-1.0, -1.0, -1.0,
			-1.0, -1.0, -1.0 );
	Matrix C(4,4);
	Matrix result = initMatrix( 3, 3,
			0.2, 2.2, 4.2,
			3.3, 4.3, 5.3,
			2.4, 2.4, 7.4 );
	ASSERT_NO_THROW(A += B);
	ASSERT_EQUALS(A,result);
	ASSERT_THROW(MatrixExceptions::BadDimensions, C+=B);
	ASSERT_THROW(MatrixExceptions::BadDimensions, B+=C);
	return true;
}
bool testMinusEqualOperator() {
	Matrix A = initMatrix( 2, 2,
			1.2, 3.2,
			4.3, 5.3);
	Matrix B = initMatrix( 2, 2,
			1.0, 1.0,
			1.0, 1.0 );
	Matrix C(5,5);
	Matrix result = initMatrix( 2, 2,
			0.2, 2.2,
			3.3, 4.3 );
	ASSERT_NO_THROW(A -= B);
	ASSERT_EQUALS(A,result);
	ASSERT_THROW(MatrixExceptions::BadDimensions, C-=B);
	ASSERT_THROW(MatrixExceptions::BadDimensions, B-=C);
	return true;
}
bool testMultiplyEqualOperator() {
	//multiply matrices
	Matrix A = initMatrix( 2, 2,
			1.0, 3.0,
			4.0, 5.0);
	Matrix B = initMatrix( 2, 2,
			1.0, 1.0,
			1.0, 1.0 );
	Matrix C = initMatrix( 2, 3,
			1.0, 1.0, 1.0,
			1.0, 1.0, 1.0 );
	Matrix D(5,5);
	Matrix resultAB = initMatrix( 2, 2,
			4.0, 4.0,
			9.0, 9.0 );
	ASSERT_NO_THROW(A *= B);
	ASSERT_EQUALS(A,resultAB);
	ASSERT_THROW(MatrixExceptions::BadDimensions, C*=A);
	ASSERT_THROW(MatrixExceptions::BadDimensions, D*=B);
	ASSERT_THROW(MatrixExceptions::BadDimensions, B*=D);
	return true;
}
bool testMultiplyEqualByScalarOperator() {
	//multiply by scalar
	Matrix A = initMatrix( 2, 2,
			1.2, 3.2,
			4.3, 5.3);
	double num = 2.0;
	Matrix result = initMatrix( 2, 2,
			2.4, 6.4,
			8.6, 10.6 );
	ASSERT_NO_THROW(A *= num);
	ASSERT_EQUALS(A,result);
	return true;
}
bool testMultiplyByScalarRightOperator() {
	Matrix A = initMatrix( 2, 2,
			2.2, 2.2,
			2.2, 2.2);
	double num = 2.0;
	Matrix result = initMatrix( 2, 2,
			4.4, 4.4,
			4.4, 4.4 );
	ASSERT_NO_THROW(A = A * num);
	ASSERT_EQUALS(A,result);
	return true;
}
bool testSubtractOperator() {
	Matrix A = initMatrix( 2, 2,
			1.2, 3.2,
			4.3, 5.3);
	Matrix B = initMatrix( 2, 2,
			1.0, 1.0,
			1.0, 1.0 );
	Matrix C(5,5);
	Matrix result = initMatrix( 2, 2,
			0.2, 2.2,
			3.3, 4.3 );
	ASSERT_NO_THROW(A = A - B);
	ASSERT_EQUALS(A,result);
	ASSERT_THROW(MatrixExceptions::BadDimensions, C = C - B);
	ASSERT_THROW(MatrixExceptions::BadDimensions, B = B - C);
	return true;
}
bool testCreateUnit() {
	Matrix A = Matrix::createUnit(5);
	for (int row = 1; row <= A.rows(); row++)
	{
		for (int col = 1; col <= A.columns(); col++)
		{
			ASSERT_EQUALS(true, IS_DOUBLE_EQUAL(A(row,col),
									row == col ? 1.0 : 0.0));
		}
	}
	ASSERT_EQUALS(A.transpose(),A.inverse());
	return true;
}
bool testTranspose() {
	Matrix A = Matrix::createUnit(3);
	Matrix B = initMatrix( 3, 4,
			1.0, 5.0, 7.0, 9.0,
			3.0, 5.0, 2.0, 8.0,
			1.0, 1.0, 1.0, 1.0 );
	Matrix result = initMatrix( 4, 3,
			1.0, 3.0, 1.0,
			5.0, 5.0, 1.0,
			7.0, 2.0, 1.0,
			9.0, 8.0, 1.0 );
	ASSERT_EQUALS(A.transpose(),A);
	ASSERT_EQUALS(B.transpose(),result);
	return true;
}
bool testInverse() {
	Matrix A = Matrix::createUnit(3);
	Matrix B = initMatrix( 3, 3,
			1.0, 2.0, 3.0,
			4.0, 5.0, 6.0,
			7.0, 8.0, 8.0);
	Matrix C = initMatrix( 2, 2,
			1.4, -4.7,
			1.4, -4.7 );
	Matrix D(5,6);
	Matrix result = initMatrix( 3, 3,
			-2.66666666666, 2.66666666666, -1.0,
			3.33333333333, -4.33333333333, 2.0,
			-1.0, 2.0, -1.0);

	ASSERT_EQUALS(A.transpose(),A.inverse());
	Matrix inversed = B.inverse();
	ASSERT_EQUALS(inversed, result);
	ASSERT_EQUALS(A, B*inversed);
	ASSERT_THROW(MatrixExceptions::BadDimensions, D.inverse());
	ASSERT_THROW(MatrixExceptions::Singular, C.inverse());

	return true;
}
bool testAddOperator() {
	Matrix A = initMatrix( 2, 2,
			1.2, 3.2,
			4.3, 5.3);
	Matrix B = initMatrix( 2, 2,
			2.0, 2.0,
			2.0, 2.0 );
	Matrix C(4,5);
	Matrix result = initMatrix( 2, 2,
			3.2, 5.2,
			6.3, 7.3 );
	ASSERT_NO_THROW(A = A + B);
	ASSERT_EQUALS(A,result);
	ASSERT_THROW(MatrixExceptions::BadDimensions, C = C + B);
	ASSERT_THROW(MatrixExceptions::BadDimensions, B = B + C);
	return true;
}
bool testMinusOperator() {
	Matrix A = initMatrix( 2, 2,
			1.2, 3.2,
			4.3, 5.3);
	Matrix B = initMatrix( 2, 2,
			-1.2, -3.2,
			-4.3, -5.3);
	Matrix C(2,2);
	ASSERT_NO_THROW(C = -A);
	ASSERT_EQUALS(C,B);
	return true;
}
bool testMultiplyOperator() {
	Matrix A = initMatrix( 2, 2,
			1.0, 3.0,
			4.0, 5.0);
	Matrix B = initMatrix( 2, 2,
			1.0, 1.0,
			1.0, 1.0 );
	Matrix C = initMatrix( 2, 3,
			1.0, 1.0, 1.0,
			1.0, 1.0, 1.0 );
	Matrix D(5,5);
	Matrix resultAB = initMatrix( 2, 2,
			4.0, 4.0,
			9.0, 9.0 );
	ASSERT_NO_THROW(A = A * B);
	ASSERT_EQUALS(A,resultAB);
	ASSERT_THROW(MatrixExceptions::BadDimensions, C=C*A);
	ASSERT_THROW(MatrixExceptions::BadDimensions, D=D*B);
	ASSERT_THROW(MatrixExceptions::BadDimensions, B=B*D);
	return true;
}
bool testMultiplyByScalarLeftOperator() {
	Matrix A = initMatrix( 2, 2,
			2.2, 2.2,
			2.2, 2.2);
	double num = 3.0;
	Matrix result = initMatrix( 2, 2,
			6.6, 6.6,
			6.6, 6.6 );
	ASSERT_NO_THROW(A = num * A);
	ASSERT_EQUALS(A,result);
	return true;
}

int main()
{
	RUN_TEST( testPlacementLeftVector );
	RUN_TEST( testPlacementRightVector );
    RUN_TEST( testDestructMatrix );
	RUN_TEST( testEqualOperator );
	RUN_TEST( testNotEqualOperator );
	RUN_TEST( testAssignmentOperator );
	RUN_TEST( testPlusEqualOperator );
	RUN_TEST( testMinusEqualOperator );
	RUN_TEST( testMultiplyEqualOperator );
	RUN_TEST( testMultiplyEqualByScalarOperator );
	RUN_TEST( testMultiplyByScalarRightOperator );
	RUN_TEST( testSubtractOperator );
	RUN_TEST( testCreateUnit );
	RUN_TEST( testTranspose );
	RUN_TEST( testInverse );
	RUN_TEST( testAddOperator );
	RUN_TEST( testMinusOperator );
	RUN_TEST( testMultiplyOperator );
	RUN_TEST( testMultiplyByScalarLeftOperator );

	return 0;
}

